package com.gzhryc.shared_device.common.device.services;

import com.gzhryc.common.StringTools;
import com.gzhryc.common.exceptions.LogicException;
import com.gzhryc.common.json.JsonTools;
import com.gzhryc.common.logging.Logger;
import com.gzhryc.jdbc.Conditions;
import com.gzhryc.jdbc.JdbcSession;
import com.gzhryc.jdbc.MultiDBTools;
import com.gzhryc.shared_device.base.models.IUser;
import com.gzhryc.shared_device.common.device.dao.DeviceProduceBatchDao;
import com.gzhryc.shared_device.common.device.dao.db.Device;
import com.gzhryc.shared_device.common.device.dao.db.DeviceProduceBatch;
import com.gzhryc.shared_device.common.device.dao.db.DeviceType;
import com.gzhryc.shared_device.common.device.dao.db.DeviceTypeFunc;
import com.gzhryc.shared_device.common.device.dao.enums.DeviceProduceBatchEnum;
import com.gzhryc.shared_device.common.device.services.dto.DeviceProduceBatchSearch;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class DeviceProduceBatchService {

    static Logger log = Logger.getLogger(DeviceProduceBatchService.class);

    public static String[] ExcludeString = new String[]{"0000","1111","2222","3333","4444","5555","6666","7777","8888","9999","1234"};

    public static DeviceProduceBatchService self() {
        return MultiDBTools.getService(DeviceProduceBatchService.class, true);
    }

    public static DeviceProduceBatchService self(String jdbcKey) {
        return MultiDBTools.getService(jdbcKey, DeviceProduceBatchService.class, true);
    }

    DeviceProduceBatchDao dao;

    public DeviceProduceBatchService(String jdbcKey) {
        dao = new DeviceProduceBatchDao(jdbcKey);
    }

    public List<DeviceProduceBatch> findBySearch(DeviceProduceBatchSearch search, Integer pageNo, Integer pageLimit){
        Conditions conditions = dao.buildConditions(search);
        conditions.and().ge("createDate",search.getStartDate());
        conditions.and().le("createDate",search.getEndDate());
        conditions.desc("createDate");
        try {
            return dao.findByCondition(conditions,pageNo,pageLimit);
        } catch (SQLException e) {
            log.error(e.getMessage(),e);
        }
        return null;
    }

    public Long countBySearch(DeviceProduceBatchSearch search){
        Conditions conditions = dao.buildConditions(search);
        conditions.and().ge("createDate",search.getStartDate());
        conditions.and().le("createDate",search.getEndDate());
        try {
            return dao.count(conditions);
        } catch (SQLException e) {
            log.error(e.getMessage(),e);
        }
        return 0L;
    }

    public DeviceProduceBatch getById(String id){
        try {
            return dao.getById(id);
        } catch (SQLException e) {
            log.error(e.getMessage(),e);
        }
        return null;
    }

    public DeviceProduceBatch getJoinById(String id){
        try {
            return dao.getJoinById(id);
        } catch (SQLException e) {
            log.error(e.getMessage(),e);
        }
        return null;
    }

    public boolean add(DeviceType deviceType,Integer startNum,Integer produceTotal) throws LogicException {
        Integer index = deviceType.getProduceBatchIndex() + 1;
        if (DeviceTypeService.self().increaseProduceBatchIndex(deviceType.getId(), deviceType.getProduceBatchIndex(), index)) {
            String batchId = deviceType.getId().toUpperCase() + buildBatchId(index);

            DeviceProduceBatch produceBatch = new DeviceProduceBatch();
            produceBatch.setBatchId(batchId);
            produceBatch.setName(deviceType.getName() + "第" + index + "生产批次");
            produceBatch.setDeviceTypeId(deviceType.getId());
            produceBatch.setStartNum(startNum);
            produceBatch.setProduceTotal(produceTotal);
            produceBatch.setCreateDate(new Date());

            try {
                return dao.insert(produceBatch) > 0;
            } catch (SQLException e) {
                log.error(e.getMessage(), e);
            }
        } else {
            throw new LogicException("设备类型生产批次索引冲突");
        }
        return false;
    }

    /**
     * 导入新增批次
     * @param deviceType
     * @param batchId
     * @param startNum
     * @param deviceList
     * @return
     * @throws LogicException
     */
    public boolean addByImport(DeviceType deviceType,String batchId,Integer startNum,List<Device> deviceList) throws LogicException {
        Integer index = deviceType.getProduceBatchIndex() + 1;
        if (DeviceTypeService.self().increaseProduceBatchIndex(deviceType.getId(), deviceType.getProduceBatchIndex(), index)) {
            DeviceProduceBatch produceBatch = new DeviceProduceBatch();
            produceBatch.setBatchId(batchId);
            produceBatch.setName(deviceType.getName() + "第" + index + "生产批次");
            produceBatch.setDeviceTypeId(deviceType.getId());
            produceBatch.setStartNum(startNum);
            produceBatch.setProduceTotal(deviceList.size());
            produceBatch.setState(DeviceProduceBatchEnum.State.Producing.index());
            produceBatch.setCreateDate(new Date());

            JdbcSession.begin();
            try {
                if (dao.insert(produceBatch) > 0) {
                    if (DeviceService.self().add(deviceList)) {
                        JdbcSession.commit();
                        return true;
                    }
                }
            } catch (SQLException e) {
                log.error(e.getMessage(), e);
            } finally {
                JdbcSession.end();
            }
        } else {
            throw new LogicException("设备类型生产批次索引冲突");
        }
        return false;
    }

    public boolean updateNotNull(DeviceProduceBatch deviceProduceBatch)throws LogicException{
        try {
            return dao.updateNotNull(deviceProduceBatch) > 0;
        } catch (SQLException e) {
            log.error(e.getMessage(),e);
        }
        return false;
    }

    public boolean setBelong(String batchId, IUser user, Integer strategyType,Integer setStartNum,Integer setTotal) throws LogicException {
        if(StringTools.isNotBlank(batchId) && user != null && strategyType != null && setStartNum != null && setTotal != null){
            DeviceProduceBatch produceBatch = getById(batchId);
            if(produceBatch == null){
                throw new LogicException("未找到设备批次信息");
            }

            if(produceBatch.getState() >= DeviceProduceBatchEnum.State.Producing.index()){
            	if(produceBatch.getStartNum().equals(setStartNum) && produceBatch.getProduceTotal().equals(setTotal)) {
            		//全部设置
            		return DeviceService.self().setBelong(batchId,user,strategyType);
            	}else {
	            	List<String> deviceSnList = getDeviceSnList(produceBatch,setStartNum,setTotal);
	            	//批量设置
            		return DeviceService.self().setBelong(deviceSnList,user,strategyType);
            	}
            }else{
                throw new LogicException("当前状态无法设置归属");
            }
        }
        return false;
    }
    
    public List<String> getDeviceSnList(DeviceProduceBatch produceBatch,Integer setStartNum,Integer setTotal) throws LogicException{
    	//检查起始位置
    	if(produceBatch.getStartNum() > setStartNum) {
    		throw new LogicException("设备批次起始位置小于设置的起始位置");
    	}
    	Integer endNum = produceBatch.getStartNum() + produceBatch.getProduceTotal();
    	Integer setEndNum = setStartNum + setTotal;
    	
    	if(endNum < setEndNum) {
    		throw new LogicException("设置的数量超过设备批次数量");
    	}
    	
    	List<String> deviceSnList = new ArrayList<String>();
     	//批量循环
    	for(int i = 0;i<setTotal;i++) {
    		 String temp = String.format("%04d", setStartNum + i);
    		 if (!ArrayUtils.contains(ExcludeString, temp)) {
                 String deviceSn = produceBatch.getBatchId() + temp;
                 deviceSnList.add(deviceSn);
    		 }
    	}
        return deviceSnList;
    }

    public boolean produceDevice(DeviceProduceBatch produceBatch) throws LogicException{
        if(produceBatch.getDeviceType() == null) {
            DeviceType deviceType = DeviceTypeService.self().getById(produceBatch.getDeviceTypeId());
            if(deviceType == null){
                throw new LogicException("未找到【"+ produceBatch.getDeviceTypeId() + "】设备类型");
            }
            produceBatch.setDeviceType(deviceType);
        }

        List<DeviceTypeFunc> deviceTypeFuncList = DeviceTypeFuncService.self().findByTypeId(produceBatch.getDeviceTypeId());
        if(deviceTypeFuncList == null || deviceTypeFuncList.size() <= 0){
            throw new LogicException(produceBatch.getDeviceTypeId() + "设备类型未配置功能项");
        }
        String deviceFunCodes = "";
        String deviceFunNames = "";
        for(DeviceTypeFunc deviceTypeFunc : deviceTypeFuncList) {
            deviceFunCodes = deviceFunCodes + "," + deviceTypeFunc.getCode();
            deviceFunNames = deviceFunNames + "," + deviceTypeFunc.getCodeName();
        }

        deviceFunCodes = deviceFunCodes.substring(1);
        deviceFunNames = deviceFunNames.substring(1);

        if(waitToProducing(produceBatch.getBatchId())) {
            try {
                List<Device> deviceList = new ArrayList<>();
                for (int i = 0; i < produceBatch.getProduceTotal(); i++) {
                    String temp = String.format("%04d", produceBatch.getStartNum() + i);
                    if (!ArrayUtils.contains(ExcludeString, temp)) {
                        String deviceSn = produceBatch.getBatchId() + temp;
                        Device device = new Device();
                        device.setDeviceSn(deviceSn);
                        device.setDeviceTypeId(produceBatch.getDeviceTypeId());
                        device.setDeviceTypeName(produceBatch.getDeviceType().getName());
                        device.setNetworkType(produceBatch.getDeviceType().getNetworkType());
                        device.setDeviceFunCodes(deviceFunCodes);
                        device.setDeviceFunNames(deviceFunNames);
                        device.setBatchId(produceBatch.getBatchId());
                        device.setCreateDate(new Date());
                        deviceList.add(device);
                    }
                }
                if (DeviceService.self().add(deviceList)) {
                    return true;
                }else{
                    updateState(produceBatch.getBatchId(),DeviceProduceBatchEnum.State.WaitProduct.index());
                }
            } catch (Exception e) {
                updateState(produceBatch.getBatchId(),DeviceProduceBatchEnum.State.WaitProduct.index());
                log.error(e.getMessage(), e);
            }
        }
        return false;
    }

    public boolean producingToTesting(String batchId,Map<String, Object> configContent) throws LogicException {
        if(StringTools.isNotBlank(batchId)) {
            Conditions conditions = new Conditions(DeviceProduceBatch.class);
            conditions.and().is("batchId",batchId);
            conditions.and().is("state", DeviceProduceBatchEnum.State.Producing.index());

            DeviceProduceBatch updateEntity = new DeviceProduceBatch();
            updateEntity.setState(DeviceProduceBatchEnum.State.Testing.index());
            if(configContent != null) {
                updateEntity.setConfigContent(JsonTools.toJson(configContent));
            }
            try {
                return dao.updateNotNull(updateEntity,conditions) > 0;
            } catch (SQLException e) {
                log.error(e.getMessage(),e);
            }
        }
        return false;
    }

    public boolean waitToProducing(String batchId){
        if(StringTools.isNotBlank(batchId)) {
            Conditions conditions = new Conditions(DeviceProduceBatch.class);
            conditions.and().is("batchId",batchId);
            conditions.and().is("state", DeviceProduceBatchEnum.State.WaitProduct.index());

            DeviceProduceBatch updateEntity = new DeviceProduceBatch();
            updateEntity.setState(DeviceProduceBatchEnum.State.Producing.index());

            try {
                return dao.updateNotNull(updateEntity,conditions) > 0;
            } catch (SQLException e) {
                log.error(e.getMessage(),e);
            }
        }
        return false;
    }

    public boolean updateState(String batchId,Integer state){
        if(StringTools.isNotBlank(batchId) && state != null){
            DeviceProduceBatch updateEntity = new DeviceProduceBatch();
            updateEntity.setBatchId(batchId);
            updateEntity.setState(state);

            try {
                return dao.updateNotNull(updateEntity) > 0;
            } catch (SQLException e) {
                log.error(e.getMessage(),e);
            }
        }
        return false;
    }

    public boolean deleteByBatchId(String batchId) throws LogicException {
        DeviceProduceBatch temp = getById(batchId);
        if(temp == null || DeviceProduceBatchEnum.State.Stock.index().equals(temp.getState())
                || DeviceProduceBatchEnum.State.Testing.index().equals(temp.getState())){
            throw new LogicException("批次不存在或已生产");
        }

        JdbcSession.begin();
        try {
            if(dao.deleteById(batchId) > 0){
                if(DeviceService.self().deleteByBatchId(batchId)){
                    JdbcSession.commit();
                    return true;
                }
            }
        }catch (SQLException e){
            log.error(e.getMessage(),e);
        } finally {
            JdbcSession.end();
        }
        return false;
    }

    private String buildBatchId(Integer index){
        String result = DateFormatUtils.format(new Date(),"yyMM");
        if(index > 0 && index < 10){
            result = result + 000 +index;
        }else if(index < 10000){
            String temp = String.format("%04d", index);
            result = result + temp;
        }else if(index < 100000){
            Integer ccc = index % 10000;
            String temp = String.format("%04d", ccc);
            result = result + temp;
        }
        return result;
    }
}
